{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c75efab3",
   "metadata": {},
   "source": [
    "# Custom prompt template\n",
    "\n",
    "Let's suppose we want the LLM to generate English language explanations of a function given its name. To achieve this task, we will create a custom prompt template that takes in the function name as input, and formats the prompt template to provide the source code of the function.\n",
    "\n",
    "## Why are custom prompt templates needed?\n",
    "\n",
    "LangChain provides a set of [default prompt templates](/docs/modules/model_io/prompts/prompt_templates/) that can be used to generate prompts for a variety of tasks. However, there may be cases where the default prompt templates do not meet your needs. For example, you may want to create a prompt template with specific dynamic instructions for your language model. In such cases, you can create a custom prompt template."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d56ce86",
   "metadata": {},
   "source": [
    "## Creating a custom prompt template\n",
    "\n",
    "There are essentially two distinct prompt templates available - string prompt templates and chat prompt templates. String prompt templates provides a simple prompt in string format, while chat prompt templates produces a more structured prompt to be used with a chat API.\n",
    "\n",
    "In this guide, we will create a custom prompt using a string prompt template. \n",
    "\n",
    "To create a custom string prompt template, there are two requirements:\n",
    "1. It has an input_variables attribute that exposes what input variables the prompt template expects.\n",
    "2. It defines a format method that takes in keyword arguments corresponding to the expected input_variables and returns the formatted prompt.\n",
    "\n",
    "We will create a custom prompt template that takes in the function name as input and formats the prompt to provide the source code of the function. To achieve this, let's first create a function that will return the source code of a function given its name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c831e1ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "import inspect\n",
    "\n",
    "\n",
    "def get_source_code(function_name):\n",
    "    # Get the source code of the function\n",
    "    return inspect.getsource(function_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2c8f4ea",
   "metadata": {},
   "source": [
    "Next, we'll create a custom prompt template that takes in the function name as input, and formats the prompt template to provide the source code of the function.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3ad1efdc",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import StringPromptTemplate\n",
    "from pydantic import BaseModel, validator\n",
    "\n",
    "PROMPT = \"\"\"\\\n",
    "Given the function name and source code, generate an English language explanation of the function.\n",
    "Function Name: {function_name}\n",
    "Source Code:\n",
    "{source_code}\n",
    "Explanation:\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "class FunctionExplainerPromptTemplate(StringPromptTemplate, BaseModel):\n",
    "    \"\"\"A custom prompt template that takes in the function name as input, and formats the prompt template to provide the source code of the function.\"\"\"\n",
    "\n",
    "    @validator(\"input_variables\")\n",
    "    def validate_input_variables(cls, v):\n",
    "        \"\"\"Validate that the input variables are correct.\"\"\"\n",
    "        if len(v) != 1 or \"function_name\" not in v:\n",
    "            raise ValueError(\"function_name must be the only input_variable.\")\n",
    "        return v\n",
    "\n",
    "    def format(self, **kwargs) -> str:\n",
    "        # Get the source code of the function\n",
    "        source_code = get_source_code(kwargs[\"function_name\"])\n",
    "\n",
    "        # Generate the prompt to be sent to the language model\n",
    "        prompt = PROMPT.format(\n",
    "            function_name=kwargs[\"function_name\"].__name__, source_code=source_code\n",
    "        )\n",
    "        return prompt\n",
    "\n",
    "    def _prompt_type(self):\n",
    "        return \"function-explainer\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fcbf6ef",
   "metadata": {},
   "source": [
    "## Use the custom prompt template\n",
    "\n",
    "Now that we have created a custom prompt template, we can use it to generate prompts for our task."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "bd836cda",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Given the function name and source code, generate an English language explanation of the function.\n",
      "Function Name: get_source_code\n",
      "Source Code:\n",
      "def get_source_code(function_name):\n",
      "    # Get the source code of the function\n",
      "    return inspect.getsource(function_name)\n",
      "\n",
      "Explanation:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "fn_explainer = FunctionExplainerPromptTemplate(input_variables=[\"function_name\"])\n",
    "\n",
    "# Generate a prompt for the function \"get_source_code\"\n",
    "prompt = fn_explainer.format(function_name=get_source_code)\n",
    "print(prompt)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
